package ui.timeline;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.RoundRectangle2D;

import javax.swing.JComponent;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.basic.BasicSliderUI;


/**
 * UI delegate for the RangeSlider component.  RangeSliderUI paints two thumbs,
 * one for the lower value and one for the upper value.
 */
public class TwoThumbTimeLineSliderUI extends BasicSliderUI 
{
	public TwoThumbTimeLineSlider timeLineSlider;
	public TwoThumbTimeLineRangeTrackListener listener;
    /** Color of selected range. */
    private Color rangeColor;
    private int thumbHeight = 20;
    private int thumbWidth = 8;
    
    private Color boundariesThumbColor;    
    private Color boundariesThumbStrokeColor;    

    private Color mainThumbColor;    
    private Color mainThumbStrokeColor;    
    

    /** Location and size of thumb for upper value. */
    private Rectangle upperThumbRect;
    /** Indicator that determines whether upper thumb is selected. */
    private boolean upperThumbSelected;
   
    /** Indicator that determines whether upper thumb is being dragged. */
    public transient boolean upperDragging;
    /** Indicator that determines whether upper thumb is being dragged. */
    public transient boolean currentDragging;

    
    /**
     * Constructs a RangeSliderUI for the specified slider component.
     * @param b RangeSlider
     */
    public TwoThumbTimeLineSliderUI(TwoThumbTimeLineSlider b) {
        super(b);
        this.rangeColor = new Color(0.66f, 0.66f, 0.66f);
        this.boundariesThumbColor= new Color(0.66f, 0.66f, 0.66f);
        this.mainThumbColor = new Color(0.66f, 0.66f, 0.66f);

        this.boundariesThumbStrokeColor = Color.black;
        this.mainThumbStrokeColor = Color.black;
        this.timeLineSlider = b;
    }
    
    public JSlider getSlider()
    {
    	return this.slider;
    }
    
    public Rectangle getTrackRect()
    {
    	return this.trackRect;
    }
    
    public Rectangle getThumbRect()
    {
    	return this.thumbRect;
    }
    
    public boolean drawInverted()
    {
    	return super.drawInverted();
    }
    
    
    public int xPositionForValue(int value)
    {
    	return super.xPositionForValue(value);
    }
    /**
     * Installs this UI delegate on the specified component. 
     */
    @Override
    public void installUI(JComponent c) {
        setUpperThumbRect(new Rectangle());
        super.installUI(c);
    }

    /**
     * Creates a listener to handle track events in the specified slider.
     */
    @Override
    protected TrackListener createTrackListener(JSlider slider) {
    	listener = new TwoThumbTimeLineRangeTrackListener(this);
        return listener;
    }

    /**
     * Creates a listener to handle change events in the specified slider.
     */
    @Override
    protected ChangeListener createChangeListener(JSlider slider) {
        return new TwoThumbTimeLineChangeListener(this);
    }
    
    /**
     * Updates the dimensions for both thumbs. 
     */
    @Override
    protected void calculateThumbSize() {
        // Call superclass method for lower thumb size.
        //super.calculateThumbSize();
        
        thumbRect.setSize(thumbWidth + 1, thumbHeight + 1);
        
        // Set upper thumb size.
        getUpperThumbRect().setSize(thumbWidth + 1, thumbHeight + 1);
    }
    
    /**
     * Updates the locations for both thumbs.
     */
    @Override
	public void calculateThumbLocation() {
        // Call superclass method for lower thumb location.
        super.calculateThumbLocation();
        
        // Adjust upper value to snap to ticks if necessary.
    
        
        // Calculate upper thumb location.  The thumb is centered over its 
        // value on the track.
        if (slider.getOrientation() == JSlider.HORIZONTAL) {
            int upperPosition = xPositionForValue(timeLineSlider.getUpperValue());
            getUpperThumbRect().x = upperPosition - (getUpperThumbRect().width / 2);
            getUpperThumbRect().y = trackRect.y;
            
        }
        
        // Calculate upper thumb location.  The thumb is centered over its 
        // value on the track.
        if (slider.getOrientation() == JSlider.HORIZONTAL) {
            int currentPosition = xPositionForValue(timeLineSlider.getValue());
            thumbRect.x = currentPosition - (thumbRect.width / 2);
            thumbRect.y = trackRect.y;
        } 
    }
    
    /**
     * Returns the size of a thumb.
     */
    @Override
    protected Dimension getThumbSize() {
        return new Dimension(thumbWidth, thumbHeight);
    }

    /**
     * Paints the slider.  The selected thumb is always painted on top of the
     * other thumb.
     */
    @Override
    public void paint(Graphics g, JComponent c) {
        super.paint(g, c);
        
        Rectangle clipRect = g.getClipBounds();
        if (isUpperThumbSelected()) {
            if (clipRect.intersects(thumbRect)) {
                paintCurrentThumb(g);
            }
            if (clipRect.intersects(getUpperThumbRect())) {
                paintUpperThumb(g);
            }
            
        } 
        else
        {
            // Paint upper thumb first, then lower thumb.
            if (clipRect.intersects(getUpperThumbRect())) {
                paintUpperThumb(g);
            }   	
            if (clipRect.intersects(thumbRect)) {
                paintCurrentThumb(g);
            }
        }
    }
    
    /**
     * Paints the track.
     */
    @Override
    public void paintTrack(Graphics g) {
        // Draw track.
    	super.paintTrack(g);
    	
        Rectangle trackBounds = trackRect;
        
        if (slider.getOrientation() == JSlider.HORIZONTAL) {
            // Determine position of selected range by moving from the middle
            // of one thumb to the other.
            int lowerX = this.getThumbRect().x + (this.getThumbRect().width / 2);
            int upperX = this.getUpperThumbRect().x + (this.getUpperThumbRect().width / 2);
            
            // Determine track position.
            int cy = 0; //(trackBounds.height / 2) - 2;

            // Save color and shift position.
            Color oldColor = g.getColor();
            g.translate(trackBounds.x, trackBounds.y + cy);
            
            // Draw selected range.
            g.setColor(rangeColor);
            for (int y = 2; y <= this.thumbHeight - 2; y++) {
                g.drawLine(lowerX - trackBounds.x, y , upperX - trackBounds.x, y);
            }

            // Restore position and color.
            g.translate(-trackBounds.x, -(trackBounds.y + cy));
            g.setColor(oldColor);
            
        }
    }
    
    /**
     * Overrides superclass method to do nothing.  Thumb painting is handled
     * within the <code>paint()</code> method.
     */
    @Override
    public void paintThumb(Graphics g) {
        // Do nothing.
    }

    
    /**
     * Paints the thumb for the upper value using the specified graphics object.
     */
    private void paintUpperThumb(Graphics g) {
        Rectangle knobBounds = getUpperThumbRect();
        // Create graphics copy.
        Graphics2D g2d = (Graphics2D) g.create();

        // Create default thumb shape.
        Shape thumbShape = createThumbShape(this.thumbWidth, this.thumbHeight);

        // Draw thumb.
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.translate(knobBounds.x, knobBounds.y);

        g2d.setColor(this.boundariesThumbColor);
        g2d.fill(thumbShape);

        g2d.setColor(this.boundariesThumbStrokeColor);
        g2d.draw(thumbShape);

        g2d.drawLine(2 , this.thumbHeight / 2 -3 , this.thumbWidth - 2, this.thumbHeight / 2 - 3 );
        g2d.drawLine(2 , this.thumbHeight / 2    , this.thumbWidth - 2, this.thumbHeight / 2 );
        g2d.drawLine(2 , this.thumbHeight / 2 +3 , this.thumbWidth - 2, this.thumbHeight / 2 + 3 );

        // Dispose graphics.
        g2d.dispose();
    }
    
    /**
     * Paints the thumb for the upper value using the specified graphics object.
     */
    private void paintCurrentThumb(Graphics g) {
        Rectangle knobBounds = thumbRect;
        // Create graphics copy.
        Graphics2D g2d = (Graphics2D) g.create();

        // Create default thumb shape.
        Shape thumbShape = createThumbShape(this.thumbWidth, this.thumbHeight);

        // Draw thumb.
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.translate(knobBounds.x, knobBounds.y);

        g2d.setColor(this.mainThumbColor);
        g2d.fill(thumbShape);

        g2d.setColor(this.mainThumbStrokeColor);
        g2d.draw(thumbShape);

        g2d.drawLine(2 , this.thumbHeight / 2 -3 , this.thumbWidth - 2, this.thumbHeight / 2 - 3 );
        g2d.drawLine(2 , this.thumbHeight / 2    , this.thumbWidth - 2, this.thumbHeight / 2 );
        g2d.drawLine(2 , this.thumbHeight / 2 +3 , this.thumbWidth - 2, this.thumbHeight / 2 + 3 );

        // Dispose graphics.
        g2d.dispose();
    }

    /**
     * Returns a Shape representing a thumb.
     */
    private Shape createThumbShape(int width, int height) {
        // Use circular shape.
        RoundRectangle2D rect = new RoundRectangle2D.Double(0, 0, width, height, 4, 4);
        return rect;
    }
    
    /** 
     * Sets the location of the upper thumb, and repaints the slider.  This is
     * called when the upper thumb is dragged to repaint the slider.  The
     * <code>setThumbLocation()</code> method performs the same task for the
     * lower thumb.
     */
    public void setUpperThumbLocation(int x, int y) {
        Rectangle upperUnionRect = new Rectangle();
        upperUnionRect.setBounds(getUpperThumbRect());
        getUpperThumbRect().setLocation(x, y);

        SwingUtilities.computeUnion(getUpperThumbRect().x, getUpperThumbRect().y, getUpperThumbRect().width, getUpperThumbRect().height, upperUnionRect);
        slider.repaint(upperUnionRect.x, upperUnionRect.y, upperUnionRect.width, upperUnionRect.height);
    }

    
    /**
     * Moves the selected thumb in the specified direction by a block increment.
     * This method is called when the user presses the Page Up or Down keys.
     */
    public void scrollByBlock(int direction) {
        synchronized (slider) {
            int blockIncrement = (slider.getMaximum() - slider.getMinimum()) / 10;
            if (blockIncrement <= 0 && slider.getMaximum() > slider.getMinimum()) {
                blockIncrement = 1;
            }
            int delta = blockIncrement * ((direction > 0) ? POSITIVE_SCROLL : NEGATIVE_SCROLL);
            
            if (isUpperThumbSelected()) {
                int oldValue = ((ThreeThumbTimeLineSlider) slider).getUpperValue();
                ((ThreeThumbTimeLineSlider) slider).setUpperValue(oldValue + delta);
            } else {
                int oldValue = slider.getValue();
                slider.setValue(oldValue + delta);
            }
        }
    }
    
    /**
     * Moves the selected thumb in the specified direction by a unit increment.
     * This method is called when the user presses one of the arrow keys.
     */
    public void scrollByUnit(int direction) {
        synchronized (slider) {
            int delta = 1 * ((direction > 0) ? POSITIVE_SCROLL : NEGATIVE_SCROLL);
            
            if (isUpperThumbSelected()) {
                int oldValue = ((ThreeThumbTimeLineSlider) slider).getUpperValue();
                ((ThreeThumbTimeLineSlider) slider).setUpperValue(oldValue + delta);
            } else {
                int oldValue = slider.getValue();
                slider.setValue(oldValue + delta);
            }
        }       
    }

	public boolean isUpperThumbSelected() {
		return upperThumbSelected;
	}

	public void setUpperThumbSelected(boolean upperThumbSelected) {
		this.upperThumbSelected = upperThumbSelected;
	}

	public Rectangle getUpperThumbRect() {
		return upperThumbRect;
	}

	public void setUpperThumbRect(Rectangle upperThumbRect) {
		this.upperThumbRect = upperThumbRect;
	}
}